package clang.ops;

import clang.data.Expression;
import clang.data.VarType;
import clang.data.Variable;

public class CBinaryOperators {
	
	public static Variable perform(String opcode, Expression v1, Expression v2) throws Exception
	{
		if("+".equals(opcode)) return performAdd(v1.calculate(), v2.calculate());
		if("-".equals(opcode)) return performSub(v1.calculate(), v2.calculate());
		if("*".equals(opcode)) return performMulti(v1.calculate(), v2.calculate());
		if("/".equals(opcode)) return performDiv(v1.calculate(), v2.calculate());
		if("==".equals(opcode)) return performEq(v1.calculate(), v2.calculate());
		if(">".equals(opcode)) return performGr(v1.calculate(), v2.calculate());
		if("<".equals(opcode)) return performLs(v1.calculate(), v2.calculate());
		if(">=".equals(opcode)) return performGrEq(v1.calculate(), v2.calculate());
		if("<=".equals(opcode)) return performLsEq(v1.calculate(), v2.calculate());
		if("!=".equals(opcode)) return performNeq(v1.calculate(), v2.calculate());
		if("=".equals(opcode)) return performAssign(v1.calculate(), v2.calculate());
		if("&&".equals(opcode)) return performAnd(v1.calculate(), v2.calculate());
		if("||".equals(opcode)) return performOr(v1.calculate(), v2.calculate());
		if("+=".equals(opcode)) return performAddAssign(v1.calculate(), v2.calculate());
		if("-=".equals(opcode)) return performSubAssign(v1.calculate(), v2.calculate());
		if("*=".equals(opcode)) return performMultiAssign(v1.calculate(), v2.calculate());
		if("/=".equals(opcode)) return performDivAssign(v1.calculate(), v2.calculate());
		if("%=".equals(opcode)) return performModAssign(v1.calculate(), v2.calculate());
		if("%".equals(opcode)) return performMod(v1.calculate(), v2.calculate());
		if(",".equals(opcode)) return performComma(v1.calculate(), v2.calculate());
		if("[]".equals(opcode)) return performArrayIndex(v1.calculate(), v2.calculate());
		
		throw new Exception("Unknown binary operator!");
	}
	
	public static Variable performAdd(Variable v1, Variable v2) throws Exception
	{
		double res = v1.getData() + v2.getData();
		return new Variable(Variable.maxType(v1.getType(), v2.getType()), res);
	}
	
	public static Variable performSub(Variable v1, Variable v2) throws Exception
	{
		double res = v1.getData() - v2.getData();
		return new Variable(Variable.maxType(v1.getType(), v2.getType()), res);
	}
	
	public static Variable performMulti(Variable v1, Variable v2) throws Exception
	{
		double res = v1.getData() * v2.getData();
		return new Variable(Variable.maxType(v1.getType(), v2.getType()), res);
	}
	
	public static Variable performDiv(Variable v1, Variable v2) throws Exception
	{
		double res = v1.getData() / v2.getData();
		return new Variable(Variable.maxType(v1.getType(), v2.getType()), res);
	}
	
	public static Variable performEq(Variable v1, Variable v2) throws Exception
	{
		boolean res = false;
		if(v1.isInteger() && v2.isInteger())
			res = (int)v1.getData() == (int)v2.getData();
		else
			res = v1.getData() == v2.getData();
		
		return new Variable(VarType.INT, res ? 1.0 : 0.0);
	}
	
	public static Variable performGr(Variable v1, Variable v2) throws Exception
	{
		boolean res = false;
		if(v1.isInteger() && v2.isInteger())
			res = (int)v1.getData() > (int)v2.getData();
		else
			res = v1.getData() > v2.getData();
		
		return new Variable(VarType.INT, res ? 1.0 : 0.0);
	}
	
	public static Variable performLs(Variable v1, Variable v2) throws Exception
	{
		boolean res = false;
		if(v1.isInteger() && v2.isInteger())
			res = (int)v1.getData() < (int)v2.getData();
		else
			res = v1.getData() < v2.getData();
		
		return new Variable(VarType.INT, res ? 1.0 : 0.0);
	}
	
	public static Variable performGrEq(Variable v1, Variable v2) throws Exception
	{
		boolean res = false;
		if(v1.isInteger() && v2.isInteger())
			res = (int)v1.getData() >= (int)v2.getData();
		else
			res = v1.getData() >= v2.getData();
		
		return new Variable(VarType.INT, res ? 1.0 : 0.0);
	}
	
	public static Variable performLsEq(Variable v1, Variable v2) throws Exception
	{
		boolean res = false;
		if(v1.isInteger() && v2.isInteger())
			res = (int)v1.getData() <= (int)v2.getData();
		else
			res = v1.getData() <= v2.getData();
		
		return new Variable(VarType.INT, res ? 1.0 : 0.0);
	}
	
	public static Variable performNeq(Variable v1, Variable v2) throws Exception
	{
		boolean res = false;
		if(v1.isInteger() && v2.isInteger())
			res = (int)v1.getData() != (int)v2.getData();
		else
			res = v1.getData() != v2.getData();
		
		return new Variable(VarType.INT, res ? 1.0 : 0.0);
	}
	
	public static Variable performAnd(Variable v1, Variable v2) throws Exception
	{
		return new Variable(VarType.INT, v1.getBool() && v2.getBool());
	}
	
	public static Variable performOr(Variable v1, Variable v2) throws Exception
	{
		return new Variable(VarType.INT, v1.getBool() || v2.getBool());
	}
	
	public static Variable performAssign(Variable v1, Variable v2) throws Exception
	{
		v1.setData(v2);
		return new Variable(v1.getType(), v1.getData());
	}
	
	public static Variable performAddAssign(Variable v1, Variable v2) throws Exception
	{
		v1.setData(performAdd(v1, v2));
		return new Variable(v1.getType(), v1.getData());
	}
	
	public static Variable performSubAssign(Variable v1, Variable v2) throws Exception
	{
		v1.setData(performSub(v1, v2));
		return new Variable(v1.getType(), v1.getData());
	}
	
	public static Variable performMultiAssign(Variable v1, Variable v2) throws Exception
	{
		v1.setData(performMulti(v1, v2));
		return new Variable(v1.getType(), v1.getData());
	}
	
	public static Variable performDivAssign(Variable v1, Variable v2) throws Exception
	{
		v1.setData(performDiv(v1, v2));
		return new Variable(v1.getType(), v1.getData());
	}
	
	public static Variable performMod(Variable v1, Variable v2) throws Exception
	{
		return new Variable(VarType.INT, v1.getInteger() % v2.getInteger());
	}
	
	public static Variable performModAssign(Variable v1, Variable v2) throws Exception
	{
		v1.setData(performMod(v1, v2));
		return new Variable(v1.getType(), v1.getData());
	}
	
	public static Variable performComma(Variable v1, Variable v2) throws Exception
	{
		return v2;
	}
	
	public static Variable performArrayIndex(Variable v1, Variable v2) throws Exception
	{
		return Variable.getValueAtVar(v1.getInteger() + v2.getInteger());
	}
}

